package site.wtcc.originalfavor.func.utils;

import lombok.extern.slf4j.Slf4j;
import site.wtcc.originalfavor.func.exception.ResponseCode;
import site.wtcc.originalfavor.func.exception.RestException;

import java.util.List;
import java.util.concurrent.*;
import java.util.function.Function;

/**
 * @author ChengXuDong
 * @Description: 自定义的线程池超级工具简略不完善没做健壮性处理但是可以使用版
 * @date 2020/4/20 10:26
 */
@Slf4j
public class ThreadPoolUtil {

    /**
     * @Description: 多线程处理list, 非阻塞式, 主线程不等待子线程执行
     * @author ChengXuDong
     * @date 2020/4/20 10:29
     */
    public static <T> Future<?> dealListAsync(List<T> list, Function<T, Object> fun, int threadCount) {
        Future<?> submitResult = null;
        try {
            if (list == null || list.isEmpty() || fun == null) {
                throw new RestException(ResponseCode.NULL_ARGUMENT);
            }
            ThreadPoolExecutor threadPool = getThreadPool();
            if (threadPool == null) {
                throw new RestException("创建的线程池为null~");
            }
            //自定义线程数量
            if (threadCount > 0) {
                threadPool.setCorePoolSize(threadCount);
            }
            for (T t : list) {
                submitResult = threadPool.submit(new Runnable() {
                    private T t;
                    private Function<T, Object> function;

                    @Override
                    public void run() {
                        try {
                            function.apply(t);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                    private Runnable setParam(T t, Function<T, Object> function) {
                        this.t = t;
                        this.function = function;
                        return this::run;
                    }
                }.setParam(t, fun));
            }
            threadPool.shutdown();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return submitResult;
    }


    /**
     * @Description: 多线程处理list, 阻塞式, 主线程等待所有任务完成
     * @author ChengXuDong
     * @date 2020/7/28 16:25
     */
    public static <T> Future<?> dealListSync(List<T> list, Function<T, Object> fun, int threadCount) {
        Future<?> submitResult = null;
        try {
            if (list == null || list.isEmpty() || fun == null) {
                throw new RestException(ResponseCode.NULL_ARGUMENT);
            }
            ThreadPoolExecutor threadPool = getThreadPool();
            if (threadPool == null) {
                throw new RestException("创建的线程池为null~");
            }
            if (threadCount > 0) {
                threadPool.setCorePoolSize(threadCount);
            }
            for (T t : list) {
                submitResult = threadPool.submit(new Runnable() {
                    private T t;
                    private Function<T, Object> function;

                    @Override
                    public void run() {
                        try {
                            function.apply(t);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                    private Runnable setParam(T t, Function<T, Object> function) {
                        this.t = t;
                        this.function = function;
                        return this::run;
                    }
                }.setParam(t, fun));
            }
            threadPool.shutdown();
            //等待所有任务都执行结束
            while (true) {
                if (threadPool.isTerminated()) {
                    log.info("所有子任务都已完成~");
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return submitResult;
    }

    /**
     * 在代码中永远不应该忽略interruptedexception，
     * 在这种情况下，简单地将异常记录为“忽略”。
     * InterruptedException的抛出会清除线程的中断状态，
     * 因此，如果异常没有得到正确处理，那么线程被中断的信息就会丢失。
     * 相反，应该重新抛出interruptedexception(立即或在清除方法状态之后)，
     * 或者通过调用thread .interrupt()重新中断线程，即使这应该是一个单线程应用程序。
     * 任何其他操作过程都有延迟线程关闭的风险，并丢失线程被中断的信息——可能没有完成它的任务。
     *
     * @Description: 创建线程池
     * @author ChengXuDong
     * @date 2020/4/20 11:12
     */
    private static ThreadPoolExecutor getThreadPool() {
        ThreadPoolExecutor threadPool = null;
        try {
            //创建实例之前可能会有一些准备性的耗时工作
            Thread.sleep(300);
            // 获取处理器数量
            int cpuNum = Runtime.getRuntime().availableProcessors();
            // 根据cpu数量,计算出合理的线程并发数
            int threadNum = cpuNum * 2 + 1;
            threadPool = new ThreadPoolExecutor(
                    threadNum - 1,
                    threadNum,
                    Integer.MAX_VALUE,
                    TimeUnit.MILLISECONDS,
                    new LinkedBlockingDeque<>(Integer.MAX_VALUE),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.AbortPolicy()
            );
        } catch (InterruptedException e) {
            log.error("创建线程池异常", e);
            // Restore interrupted state...
            Thread.currentThread().interrupt();
        }
        return threadPool;
    }

}
